import Const from '../../common/const';
import Utils from '../../common/Utils';
import ImageTileMapMaterial from './Material/ImageTileMapMaterial';
import PassMessageMask from './MessageMask/PassMessageMask';


export default class TileMap {

    messageMasks = {};

    constructor(canvasContext) {
        this.canvasContext = canvasContext;
        this.materialCache = {};
    }

    async setConfig(opt) {

        if (opt == null) {
            return;
        }

        let config = this.config || {
            name: '未命名地图',
            tileWidth: Const.TileWidth,
            tileHeight: Const.TileHeight,
            width: Const.DefaultMapWidth,
            height: Const.DefaultMapHeight,
            type: 'default',
            objects: [],
            materials: [],
            cells: []
        };

        Utils.minerAssign(this.config, opt);

        this.initCells(config);

        await this.loadMaterial(config);

        this.opt = opt;
        this.confg = config;
    }

    initCells(config) {
        config.cells = [];
        for (let i = 0; i < config.height; ++i) {
            let row = config.cells[i] = [];
            for (let j = 0; j < config.width; ++j) {
                let cell = {
                    pos: [j, i], //采用 x, y 坐标，故 i, j 逆序
                    tile: [],
                    object: [],
                    dirty: false
                };
                row.push(cell);
            }
        }
    }

    async loadMaterial(config) {
        let ret = config.materials
            .filter(e => e && e.type)
            .parallelForeach(async res => {
                let TileMaterial = this.getMaterial(res.type);
                let uniqueKey = TileMaterial.getUniqueKey ? TileMaterial.getUniqueKey(res) :
                    TileMaterial.prototype.getUniqueKey ? TileMaterial.prototype.getUniqueKey(res) :
                    undefined;
                let tileMaterial = this.materialCache[uniqueKey];
                if (tileMaterial == null) {
                    tileMaterial = new TileMaterial(this);
                    tileMaterial.uniqueKey = uniqueKey;
                }
                await tileMaterial.load(res);
                if (Reflect.getPrototypeOf(res) !== tileMaterial) {
                    Reflect.setPrototypeOf(res, tileMaterial);
                }
                this.materialCache[uniqueKey] = tileMaterial;
                return res;
            });
        return await ret;
    }

    draw() {
        try {
            this.canvasContext.save();
            let config = this.config;
            let canvasWidth = config.width * config.tileWidth;
            let canvasHeight = config.height * config.tileHeight;
            this.canvasContext.clearRect(0, 0, canvasWidth, canvasHeight);
            //TODO draw backgroud
            config.cells.forEach(row => {
                row.forEach(cell => {
                    //TODO sort tiles
                    cell.tiles.forEach((tile, index) => {
                        let res = this.config.materials[tile.res];
                        res.draw(cell, tile, {
                            field: 'tile',
                            index
                        });
                    });
                    //TODO draw objectsddd
                });
            });


            let mm = this.getMessageMask(this.config.messageMaskType);

            if (mm != null) {
                if (this.config.messageMaskType && this.config.messageMaskType != 'none') {
                    this.canvasContext.fillStyle = 'black';
                    this.canvasContext.globalAlpha = 0.5;
                    this.canvasContext.fillRect(0, 0, canvasWidth, canvasHeight);
                }

                config.cells.forEach(row => {
                    row.forEach(cell => {
                        mm.draw(cell);
                    });
                });
            }
        } finally {
            this.canvasContext.restore();
        }
    }

    getMessageMask(type) {
        let masks = this.messageMasks || {};
        if (masks[type]) {
            return masks[type];
        }
        switch (type) {
            case 'pass':
                return masks[type] = new PassMessageMask(this);
        }
    }

    getMaterial(type) {
        switch (type) {
            case 'image':
                return ImageTileMapMaterial;
        }
    }

    onClick(e) {
        let j = Math.floor(e.x / this.config.tileWidth);
        let i = Math.floor(e.y / this.config.tileHeight);
        let offsetX = e.x % this.config.tileWidth;
        let offsetY = e.y % this.config.tileHeight;
        let mask = this.getMessageMask(this.config.messageMaskType);
        let cells = this.config.cells;
        if (mask && cells && cells[i] && cells[i][j]) {
            mask.onClick(cells[i][j], { x: e.x, y: e.y, offsetX, offsetY });
        }
        
    }
}
